home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
src.arc
/
TCPSUBR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-19
|
9KB
|
403 lines
#include <stdio.h>
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
#include "ip.h"
static int16 hash_tcb __ARGS((struct connection *conn));
/* TCP connection states */
char *Tcpstates[] = {
"Closed",
"Listen",
"SYN sent",
"SYN received",
"Established",
"FIN wait 1",
"FIN wait 2",
"Close wait",
"Closing",
"Last ACK",
"Time wait"
};
/* TCP closing reasons */
char *Tcpreasons[] = {
"Normal",
"Reset/Refused",
"Timeout",
"ICMP"
};
struct tcb *Tcbs[NTCB];
int16 Tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
int32 Tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
int Tcp_trace; /* State change tracing flag */
struct tcp_rtt Tcp_rtt[RTTCACHE];
/* Lookup connection, return TCB pointer or NULLTCB if nonexistant */
struct tcb *
lookup_tcb(conn)
struct connection *conn;
{
register struct tcb *tcb;
tcb = Tcbs[hash_tcb(conn)];
while(tcb != NULLTCB){
/* Yet another structure compatibility hack */
if(conn->local.address == tcb->conn.local.address
&& conn->remote.address == tcb->conn.remote.address
&& conn->local.port == tcb->conn.local.port
&& conn->remote.port == tcb->conn.remote.port)
break;
tcb = tcb->next;
}
return tcb;
}
/* Create a TCB, return pointer. Return pointer if TCB already exists. */
struct tcb *
create_tcb(conn)
struct connection *conn;
{
register struct tcb *tcb;
struct tcp_rtt *tp;
if((tcb = lookup_tcb(conn)) != NULLTCB)
return tcb;
if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB)
return NULLTCB;
ASSIGN(tcb->conn,*conn);
tcb->cwind = tcb->mss = Tcp_mss;
tcb->ssthresh = 65535;
if((tp = rtt_get(tcb->conn.remote.address)) != NULLRTT){
tcb->srtt = tp->srtt;
tcb->mdev = tp->mdev;
} else {
tcb->srtt = Tcp_irtt; /* mdev = 0 */
}
/* Initialize timer intervals */
tcb->timer.start = tcb->srtt / MSPTICK;
tcb->timer.func = tcp_timeout;
tcb->timer.arg = tcb;
link_tcb(tcb);
return tcb;
}
/* Close our TCB */
void
close_self(tcb,reason)
register struct tcb *tcb;
int reason;
{
struct reseq *rp1;
register struct reseq *rp;
if(tcb == NULLTCB)
return;
stop_timer(&tcb->timer);
tcb->reason = reason;
/* Flush reassembly queue; nothing more can arrive */
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
free_p(rp->bp);
free((char *)rp);
}
tcb->reseq = NULLRESEQ;
setstate(tcb,CLOSED);
}
/* Sequence number comparisons
* Return true if x is between low and high inclusive,
* false otherwise
*/
int
seq_within(x,low,high)
register int32 x,low,high;
{
if(low <= high){
if(low <= x && x <= high)
return 1;
} else {
if(low >= x && x >= high)
return 1;
}
return 0;
}
int
seq_lt(x,y)
register int32 x,y;
{
return (long)(x-y) < 0;
}
#ifdef notdef
int
seq_le(x,y)
register int32 x,y;
{
return (long)(x-y) <= 0;
}
#endif /* notdef */
int
seq_gt(x,y)
register int32 x,y;
{
return (long)(x-y) > 0;
}
int
seq_ge(x,y)
register int32 x,y;
{
return (long)(x-y) >= 0;
}
/* Hash a connect structure into the hash chain header array */
static int16
hash_tcb(conn)
struct connection *conn;
{
register unsigned int hval;
/* Compute hash function on connection structure */
hval = hiword(conn->remote.address);
hval ^= loword(conn->remote.address);
#ifdef notdef /* Never changes, so not really needed */
hval ^= hiword(conn->local.address);
hval ^= loword(conn->local.address);
#endif
hval ^= conn->remote.port;
hval ^= conn->local.port;
return hval % NTCB;
}
/* Insert TCB at head of proper hash chain */
void
link_tcb(tcb)
register struct tcb *tcb;
{
register struct tcb **tcbhead;
char i_state;
tcb->prev = NULLTCB;
i_state = dirps();
tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
tcb->next = *tcbhead;
if(tcb->next != NULLTCB)
tcb->next->prev = tcb;
*tcbhead = tcb;
restore(i_state);
}
/* Remove TCB from whatever hash chain it may be on */
void
unlink_tcb(tcb)
register struct tcb *tcb;
{
register struct tcb **tcbhead;
char i_state;
i_state = dirps();
tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
if(tcb->prev == NULLTCB)
*tcbhead = tcb->next; /* We're the first one on the chain */
else
tcb->prev->next = tcb->next;
if(tcb->next != NULLTCB)
tcb->next->prev = tcb->prev;
restore(i_state);
}
void
setstate(tcb,newstate)
register struct tcb *tcb;
register int newstate;
{
register char oldstate;
oldstate = tcb->state;
tcb->state = newstate;
if(Tcp_trace)
printf("TCB %lx %s -> %s\n",ptol(tcb),
Tcpstates[oldstate],Tcpstates[newstate]);
if(tcb->s_upcall)
(*tcb->s_upcall)(tcb,oldstate,newstate);
switch(newstate){
case ESTABLISHED:
/* Notify the user that he can begin sending data */
if(tcb->t_upcall)
(*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
break;
}
}
/* Convert TCP header in host format into mbuf ready for transmission,
* link in data (if any), and compute checksum
*/
struct mbuf *
htontcp(tcph,data,ph)
register struct tcp *tcph;
struct mbuf *data;
struct pseudo_header *ph;
{
int16 hdrlen;
struct mbuf *bp;
register char *cp;
int16 csum;
hdrlen = (tcph->mss != 0) ? TCPLEN + MSS_LENGTH : TCPLEN;
if((bp = pushdown(data,hdrlen)) == NULLBUF){
free_p(data);
return NULLBUF;
}
cp = bp->data;
cp = put16(cp,tcph->source);
cp = put16(cp,tcph->dest);
cp = put32(cp,tcph->seq);
cp = put32(cp,tcph->ack);
*cp++ = hdrlen << 2; /* Offset field */
*cp = 0;
if(tcph->flags.urg)
*cp |= 32;
if(tcph->flags.ack)
*cp |= 16;
if(tcph->flags.psh)
*cp |= 8;
if(tcph->flags.rst)
*cp |= 4;
if(tcph->flags.syn)
*cp |= 2;
if(tcph->flags.fin)
*cp |= 1;
cp++;
cp = put16(cp,tcph->wnd);
*cp++ = 0; /* Zero out checksum field */
*cp++ = 0;
cp = put16(cp,tcph->up);
if(tcph->mss != 0){
*cp++ = MSS_KIND;
*cp++ = MSS_LENGTH;
cp = put16(cp,tcph->mss);
}
csum = cksum(ph,bp,ph->length);
/* Fill checksum field */
put16(&bp->data[16],csum);
return bp;
}
/* Pull TCP header off mbuf */
int
ntohtcp(tcph,bpp)
register struct tcp *tcph;
struct mbuf **bpp;
{
int16 hdrlen;
int16 i,optlen;
register int flags;
char hdrbuf[TCPLEN];
i = pullup(bpp,hdrbuf,TCPLEN);
/* Note that the results will be garbage if the header is too short.
* We don't check for this because returned ICMP messages will be
* truncated, and we at least want to get the port numbers.
*/
tcph->source = get16(&hdrbuf[0]);
tcph->dest = get16(&hdrbuf[2]);
tcph->seq = get32(&hdrbuf[4]);
tcph->ack = get32(&hdrbuf[8]);
hdrlen = (hdrbuf[12] & 0xf0) >> 2;
flags = hdrbuf[13];
tcph->flags.urg = flags & 32;
tcph->flags.ack = flags & 16;
tcph->flags.psh = flags & 8;
tcph->flags.rst = flags & 4;
tcph->flags.syn = flags & 2;
tcph->flags.fin = flags & 1;
tcph->wnd = get16(&hdrbuf[14]);
tcph->up = get16(&hdrbuf[18]);
tcph->mss = 0;
/* Check for option field. Only space for one is allowed, but
* since there's only one TCP option (MSS) this isn't a problem
*/
if(i < TCPLEN || hdrlen < TCPLEN)
return -1; /* Header smaller than legal minimum */
if(hdrlen == TCPLEN)
return hdrlen; /* No options, all done */
if(hdrlen > len_mbuf(*bpp) + TCPLEN){
/* Remainder too short for options length specified */
return -1;
}
/* Process options */
for(i=TCPLEN; i < hdrlen;){
switch(pullchar(bpp)){
case EOL_KIND:
i++;
goto eol; /* End of options list */
case NOOP_KIND:
i++;
break;
case MSS_KIND:
optlen = pullchar(bpp);
if(optlen == MSS_LENGTH)
tcph->mss = pull16(bpp);
i += optlen;
break;
}
}
eol:
/* Get rid of any padding */
if(i < hdrlen)
pullup(bpp,NULLCHAR,hdrlen - i);
return hdrlen;
}
/* Round trip timing cache routines.
* These functions implement a very simple system for keeping track of
* network performance for future use in new connections.
* The emphasis here is on speed of update (rather than optimum cache hit
* ratio) since rtt_add is called every time a TCP connection updates
* its round trip estimate.
*/
void
rtt_add(addr,rtt)
int32 addr; /* Destination IP address */
int32 rtt;
{
register struct tcp_rtt *tp;
int32 abserr;
if(addr == 0)
return;
tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
if(tp->addr != addr){
/* New entry */
tp->addr = addr;
tp->srtt = rtt;
tp->mdev = 0;
} else {
/* Run our own SRTT and MDEV integrators, with rounding */
abserr = (rtt > tp->srtt) ? rtt - tp->srtt : tp->srtt - rtt;
tp->srtt = ((AGAIN-1)*tp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
tp->mdev = ((DGAIN-1)*tp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
}
}
struct tcp_rtt *
rtt_get(addr)
int32 addr;
{
register struct tcp_rtt *tp;
if(addr == 0)
return NULLRTT;
tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
if(tp->addr != addr)
return NULLRTT;
return tp;
}